home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1994…tember: Reference Library / Dev.CD Sep 94.toast / Periodicals / develop / develop Issue 6 / develop 6 code / TCP / NewsWatcher / NewsWatcher 2.0d15 source / source / drag.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-07-29  |  13.9 KB  |  545 lines  |  [TEXT/KAHL]

  1. /*----------------------------------------------------------------------------
  2.  
  3.     drag.c
  4.  
  5.     This module handles dragging groups within and between group windows.
  6.     
  7.     Portions copyright © 1990, Apple Computer.
  8.     Portions copyright © 1993, Northwestern University.
  9.  
  10. ----------------------------------------------------------------------------*/
  11.  
  12. #include <stdio.h>
  13.  
  14. #include "glob.h"
  15. #include "drag.h"
  16. #include "subscribe.h"
  17.  
  18.  
  19.  
  20. static Boolean gFirstCall;        /* true if first click loop call after mouse down */
  21.  
  22.  
  23.  
  24. /*----------------------------------------------------------------------------
  25.     MoveGroups
  26.     
  27.     This function handles moving groups in a user group list after the user
  28.     drags cells around in the list and releases the mouse.
  29.     
  30.     Entry:    wind = pointer to user group list window.
  31.             destRow = row number of cell following destination.
  32.     
  33.     All selected cells in the window are moved to the destination location.
  34. ----------------------------------------------------------------------------*/
  35.  
  36. static void MoveGroups (WindowPtr wind, short destRow)
  37. {
  38.     TWindow **info;
  39.     ListHandle theList;
  40.     short count;
  41.     Cell oldCell, newCell;
  42.     short dataLen, cellData, firstRow, prevRow;
  43.     Boolean contiguous = true;
  44.  
  45.     info = (TWindow**)GetWRefCon(wind);
  46.     theList = (**info).theList;
  47.     
  48.     /* Count the number of cells to be moved. Also check to see if the
  49.        cells being moved are contiguous. */
  50.     
  51.     count = 0;
  52.     SetPt(&oldCell, 0, 0);
  53.     while (LGetSelect(true, &oldCell, theList)) {
  54.         if (count == 0) firstRow = oldCell.v;
  55.         if (contiguous && count > 0 && oldCell.v != prevRow + 1) contiguous = false;
  56.         prevRow = oldCell.v;
  57.         count++;
  58.         oldCell.v++;
  59.     }
  60.     
  61.     /* If the cells being moved are contiguous, check to see if there is no
  62.        change. If not, just return. */
  63.        
  64.     if (contiguous && firstRow <= destRow && destRow <= firstRow + count) return;
  65.     
  66.     /* Insert the new cells at the destination location. */
  67.  
  68.     LDoDraw(false, theList);
  69.     LAddRow(count, destRow, theList);
  70.     
  71.     /* Copy the selected cells to their new location. */
  72.     
  73.     SetPt(&newCell, 0, destRow);
  74.     SetPt(&oldCell, 0, 0);
  75.     while (LGetSelect(true, &oldCell, theList)) {
  76.         dataLen = 2;
  77.         LGetCell(&cellData, &dataLen, oldCell, theList);
  78.         LSetCell(&cellData, dataLen, newCell, theList);
  79.         oldCell.v++;
  80.         newCell.v++;
  81.     }
  82.     
  83.     /* Delete the old cells. */
  84.  
  85.     SetPt(&oldCell, 0, 0);
  86.     while (LGetSelect(true, &oldCell, theList)) {
  87.         LDelRow(1, oldCell.v, theList);
  88.         if (oldCell.v < destRow) destRow--;
  89.     }
  90.     
  91.     /* Select the new cells. */
  92.     
  93.     SetPt(&newCell, 0, destRow);
  94.     while (count--) {
  95.         LSetSelect(true, newCell, theList);
  96.         newCell.v++;
  97.     }
  98.     
  99.     /* Redraw the window. */
  100.  
  101.     InvalRect(&wind->portRect);
  102.     LDoDraw(true, theList);
  103.     
  104.     (**info).changed = true;
  105. }
  106.  
  107.  
  108.  
  109. /*----------------------------------------------------------------------------
  110.     DrawDragLine
  111.     
  112.     Draw or erase a horizontal dividing line in a list.
  113.     
  114.     Entry:    list = handle to the list.
  115.             lineV = vertical coordinate in list of line to draw or erase.
  116. ----------------------------------------------------------------------------*/
  117.  
  118. static void DrawDragLine (ListHandle list, short lineV)
  119. {
  120.     GrafPtr savePort, listPort;
  121.     PenState savePen;
  122.     Rect clipRect;
  123.     
  124.     listPort = (**list).port;
  125.  
  126.     GetPort(&savePort);
  127.     GetPenState(&savePen);
  128.  
  129.     SetPort(listPort);
  130.     clipRect = (**list).rView;
  131.     ClipRect(&clipRect);
  132.     PenMode(patXor);
  133.     PenPat(qd.black);
  134.     PenSize(3, 3);
  135.     MoveTo((**list).rView.left, lineV);
  136.     LineTo((**list).rView.right - 3, lineV);
  137.  
  138.     SetPenState(&savePen);
  139.     SetPort(savePort);
  140. }
  141.  
  142.  
  143.  
  144. /*----------------------------------------------------------------------------
  145.     GetDestRow
  146.     
  147.     Gets the destination row in a list given a mouse position. Also determines
  148.     whether or not the list needs to be scrolled.
  149.     
  150.     Entry:    list = handle to list record.
  151.             mousePt = mouse position in local coordinates.
  152.     
  153.     Exit:    *destRow = row number of cell following destination, or 
  154.                 -10 if mouse to left or right of list.
  155.             *lineV = vertical coordinate of dividing line at destination
  156.                 in local coordinates, or -10 if mouse to left or right of list.
  157.             *scrollDelta = delta in cells that list must be scrolled
  158.                 (-1, 0, or +1).
  159. ----------------------------------------------------------------------------*/
  160.  
  161. static void GetDestRow (ListHandle list, Point mousePt, short *destRow,
  162.     short *lineV, short *scrollDelta)
  163. {
  164.     Rect rView, visible, dataBounds;
  165.     Point cellSize;
  166.     
  167.     rView = (**list).rView;
  168.     visible = (**list).visible;
  169.     dataBounds = (**list).dataBounds;
  170.     cellSize = (**list).cellSize;
  171.  
  172.     /* Check to see if the mouse is to the left or right of the list. */
  173.     
  174.     if (mousePt.h < rView.left || mousePt.h > rView.right) {
  175.         *destRow = -10;
  176.         *lineV = -10;
  177.         *scrollDelta = 0;
  178.         return;
  179.     }
  180.  
  181.     /* See if scrolling is necessary. */
  182.     
  183.     if (mousePt.v < rView.top && visible.top > dataBounds.top) {
  184.         *scrollDelta = -1;
  185.     } else if (mousePt.v > rView.bottom && visible.bottom < dataBounds.bottom) {
  186.         *scrollDelta = +1;
  187.     } else {
  188.         *scrollDelta = 0;
  189.     }
  190.  
  191.     /* Compute the destination row. */
  192.     
  193.     *destRow = (mousePt.v - rView.top + (cellSize.v >> 1)) / cellSize.v + visible.top;
  194.     if (*destRow < visible.top) *destRow = visible.top;
  195.     if (*destRow > visible.bottom) *destRow = visible.bottom;
  196.     if (*destRow > dataBounds.bottom) *destRow = dataBounds.bottom;
  197.     
  198.     /* Compute the v coordinate of the dividing line. */
  199.     
  200.     *lineV = cellSize.v * (*destRow - visible.top) + rView.top - 1;
  201.  
  202.     /* Adjust destination row for scrolling. */
  203.     
  204.     *destRow += *scrollDelta;
  205. }
  206.  
  207.  
  208.  
  209. /*----------------------------------------------------------------------------
  210.     DragMoveGroups
  211.     
  212.     This function handles dragging of groups within a user group list.
  213.     It tracks a bold dividing line at the location where the groups will
  214.     be moved if the mouse button is released.  It also handles autoscrolling.
  215.     
  216.     Entry:    wind = pointer to user group list window.
  217. ----------------------------------------------------------------------------*/
  218.  
  219. static void DragMoveGroups (WindowPtr wind)
  220. {
  221.     TWindow **info;
  222.     ListHandle theList;
  223.     Boolean lineDrawn = false;
  224.     Point mousePt;
  225.     short destRow, lineV, scrollDelta, prevLineV;
  226.  
  227.     info = (TWindow**)GetWRefCon(wind);
  228.     theList = (**info).theList;
  229.  
  230.     while (StillDown()) {
  231.     
  232.         GetMouse(&mousePt);
  233.         GetDestRow(theList, mousePt, &destRow, &lineV, &scrollDelta);
  234.         
  235.         /* Erase the old dividing line if necessary. */
  236.         
  237.         if (lineDrawn && (lineV != prevLineV || scrollDelta != 0)) {
  238.             DrawDragLine(theList, prevLineV);
  239.             lineDrawn = false;
  240.         }
  241.         
  242.         /* Autoscroll if necessary. */
  243.         
  244.         if (scrollDelta != 0) LScroll(0, scrollDelta, theList);
  245.         
  246.         /* Draw the new dividing line if necessary. */
  247.         
  248.         if (!lineDrawn && destRow >= 0) {
  249.             DrawDragLine(theList, lineV);
  250.             prevLineV = lineV;
  251.             lineDrawn = true;
  252.         }
  253.         
  254.     }
  255.     
  256.     if (lineDrawn) {
  257.         DrawDragLine(theList, lineV);
  258.         MoveGroups(wind, destRow);
  259.     }
  260. }
  261.  
  262.  
  263.  
  264. /*----------------------------------------------------------------------------
  265.     MouseIsOverUserGroupList
  266.     
  267.     Checks to see if the mouse is over a user group list.
  268.     
  269.     Exit:    function result = true if mouse is over a user group list.
  270.             *thePt = current mouse position in global coords.
  271.             if function result = true:
  272.                 *theWind = pointer to user group list window.
  273.                 *theInfo = handle to group list window info.
  274.                 *theList = handle to group list record.
  275.             if function result = false:
  276.                 *theWind, *theInfo, *theList unchanged.
  277. ----------------------------------------------------------------------------*/
  278.  
  279. static Boolean MouseIsOverUserGroupList (Point *thePt, WindowPtr *theWind,
  280.     TWindow ***theInfo, ListHandle *theList)
  281. {
  282.     WindowPtr wind;
  283.     TWindow **info;
  284.     ListHandle list;
  285.     Rect rView;
  286.     GrafPtr savePort;
  287.     Point localPt;
  288.     
  289.     GetPort(&savePort);
  290.     GetMouse(thePt);
  291.     LocalToGlobal(thePt);
  292.     FindWindow(*thePt, &wind);
  293.     if (wind != nil) {
  294.         info = (TWindow**)GetWRefCon(wind);
  295.         if ((**info).kind == kUserGroup) {
  296.             list = (**info).theList;
  297.             rView = (**list).rView;
  298.             localPt = *thePt;
  299.             SetPort(wind);
  300.             GlobalToLocal(&localPt);
  301.             if (PtInRect(localPt, &rView)) {
  302.                 *theWind = wind;
  303.                 *theInfo = info;
  304.                 *theList = list;
  305.                 SetPort(savePort);
  306.                 return true;
  307.             }
  308.         }
  309.     }
  310.     SetPort(savePort);
  311.     return false;
  312. }
  313.  
  314.  
  315.  
  316. /*----------------------------------------------------------------------------
  317.     DragSubscribeGroups
  318.     
  319.     This function handles dragging of groups from the full group list window
  320.     or new groups list window to a user group list window. 
  321.     
  322.     There are two stages to the drag:
  323.     
  324.     First, a gray region outline of the cells is dragged out of the starting
  325.     window. The user drags this gray region around the screen until the mouse 
  326.     is over a user group list window. At this point the gray region disappears 
  327.     and the first stage ends.
  328.     
  329.     In the second stage, a bold dividing line is tracked at the location where
  330.     the groups will be copied if the mouse button is released, with autoscrolling.
  331.     If the user moves the mouse to some other user group list window, that
  332.     new window becomes the target window.
  333.     
  334.     Entry:    startWind = pointer to starting group list window.
  335.             startPt = initial click location in starting window, in 
  336.                 local coordinates.
  337. ----------------------------------------------------------------------------*/
  338.  
  339. static void DragSubscribeGroups (WindowPtr startWind, Point startPt)
  340. {
  341.     WindowPtr curWind, prevWind, wind;
  342.     TWindow **startInfo, **curInfo, **info;
  343.     ListHandle startList, prevList = nil, curList;
  344.     RgnHandle grayRgn;
  345.     Cell curCell;
  346.     Rect cellRect;
  347.     Point tempPt, prevPt, curPt;
  348.     GrafPtr savePort;
  349.     GrafPort fullPort;
  350.     Boolean rgnDrawn = true, lineDrawn = false;
  351.     short lineV, prevLineV, destRow, scrollDelta;
  352.  
  353.     startInfo = (TWindow**)GetWRefCon(startWind);
  354.     startList = (**startInfo).theList;
  355.     
  356.     /* Start stage 1 - drag a gray region around. */
  357.     
  358.     /* Construct the gray region from the selected cells in the starting window. */
  359.  
  360.     grayRgn = NewRgn();
  361.     OpenRgn();
  362.     SetPt(&curCell,0,0);
  363.     while (LGetSelect(true, &curCell, startList)) {
  364.         LRect(&cellRect, curCell, startList);
  365.         FrameRect(&cellRect);
  366.         curCell.v++;
  367.     }
  368.     CloseRgn(grayRgn);
  369.     
  370.     /* Offset the gray region to the current mouse location. */
  371.     
  372.     GetMouse(&curPt);
  373.     OffsetRgn(grayRgn, curPt.h - startPt.h, curPt.v - startPt.v);
  374.     
  375.     /* Convert the gray region and the current mouse loc to global coords. */
  376.     
  377.     SetPt(&tempPt, 0, 0);
  378.     LocalToGlobal(&tempPt);
  379.     OffsetRgn(grayRgn, tempPt.h, tempPt.v);
  380.     LocalToGlobal(&curPt);
  381.     
  382.     /* Create a full screen GrafPort. */
  383.  
  384.     GetPort(&savePort);
  385.     OpenPort(&fullPort);
  386.     CopyRgn(GetGrayRgn(), fullPort.visRgn);
  387.     fullPort.portRect = (**GetGrayRgn()).rgnBBox;
  388.     PenPat(qd.gray);
  389.     PenMode(patXor);
  390.  
  391.     /* Draw the initial gray region. */
  392.     
  393.     FrameRgn(grayRgn);
  394.     prevPt = curPt;
  395.     
  396.     /* Drag the gray region around until the mouse is over a user 
  397.        group list window or the mouse is released. */
  398.     
  399.     while (StillDown()) {
  400.     
  401.         /* Break out of the loop if the mouse is over a user group list. */
  402.     
  403.         if (MouseIsOverUserGroupList(&curPt, &curWind, &curInfo, &curList)) break;
  404.         
  405.         /* Erase the old gray region if necessary. */
  406.         
  407.         if (rgnDrawn && !EqualPt(curPt, prevPt)) {
  408.             FrameRgn(grayRgn);
  409.             rgnDrawn = false;
  410.         }
  411.         
  412.         /* Draw the new gray region if necessary. */
  413.         
  414.         if (!rgnDrawn) {
  415.             OffsetRgn(grayRgn, curPt.h - prevPt.h, curPt.v - prevPt.v);
  416.             FrameRgn(grayRgn);
  417.             prevPt = curPt;
  418.             rgnDrawn = true;
  419.         }
  420.                 
  421.     }
  422.  
  423.     if (rgnDrawn) FrameRgn(grayRgn);
  424.     DisposeRgn(grayRgn);
  425.     ClosePort(&fullPort);
  426.     SetPort(savePort);
  427.     if (!StillDown()) return;
  428.     
  429.     /* Start stage 2 - track the destination dividing line, switching target
  430.        user group list windows as necessary. */
  431.     
  432.     SetPort(curWind);
  433.     
  434.     while (StillDown()) {
  435.     
  436.         /* If the mouse is in some other user group list window, make that window
  437.            the target window. */
  438.     
  439.         prevList = curList;
  440.         if (MouseIsOverUserGroupList(&curPt, &curWind, &curInfo, &curList)) SetPort(curWind);
  441.         GlobalToLocal(&curPt);
  442.  
  443.         /* Get the destination row. */
  444.         
  445.         GetDestRow(curList, curPt, &destRow, &lineV, &scrollDelta);
  446.         
  447.         /* Erase the old dividing line if necessary. */
  448.         
  449.         if (lineDrawn && (curList != prevList || scrollDelta != 0 || lineV != prevLineV)) {
  450.             DrawDragLine(prevList, prevLineV);
  451.             lineDrawn = false;
  452.         }
  453.         
  454.         /* Autoscroll if necessary. */
  455.         
  456.         if (scrollDelta != 0) LScroll(0, scrollDelta, curList);
  457.         
  458.         /* Draw the new dividing line if necessary. */
  459.         
  460.         if (!lineDrawn && destRow >= 0) {
  461.             DrawDragLine(curList, lineV);
  462.             prevLineV = lineV;
  463.             lineDrawn = true;
  464.         }
  465.         
  466.     }
  467.  
  468.     if (lineDrawn) {
  469.         DrawDragLine(prevList, prevLineV);
  470.         SubscribeSelected(startWind, curWind, destRow);
  471.     }
  472.     
  473.     SetPort(savePort);
  474. }
  475.  
  476.  
  477.  
  478. /*----------------------------------------------------------------------------
  479.     GroupListClickLoop
  480.     
  481.     The click loop routine for group list windows. It handles dragging 
  482.     groups between and within group list windows.
  483.     
  484.     Groups can be dragged from the full group list window or new groups
  485.     list window to a user group list window to subscribe to the groups.
  486.  
  487.     Groups can be dragged within a user group list window to reorder
  488.     the groups.
  489.     
  490.     Exit:    function result = true if mouse button still down, false
  491.                 if mouse button released.
  492. ----------------------------------------------------------------------------*/
  493.  
  494. pascal Boolean GroupListClickLoop (void)
  495. {    
  496.     static Point firstPt;
  497.     WindowPtr wind;
  498.     TWindow **info;
  499.     ListHandle theList;
  500.     Point thePt;
  501.     Rect rect, cellRect;
  502.     Cell cellClicked;
  503.  
  504.     if (gFirstCall) {
  505.         gFirstCall = false;
  506.         GetMouse(&firstPt);
  507.         return true;
  508.     }
  509.  
  510.     wind = FrontWindow();
  511.     info = (TWindow**)GetWRefCon(wind);
  512.     theList = (**info).theList;
  513.     
  514.     SetRect(&rect, firstPt.h - 3, firstPt.v - 3, firstPt.h + 3, firstPt.v + 3);
  515.     cellClicked = LLastClick(theList);
  516.     LRect(&cellRect, cellClicked, theList);
  517.     SectRect(&rect, &cellRect, &rect);
  518.     GetMouse(&thePt);
  519.     if (PtInRect(thePt, &rect)) return true;
  520.     
  521.     if ((**info).kind == kUserGroup) {
  522.         DragMoveGroups(wind);
  523.     } else {
  524.         DragSubscribeGroups(wind, firstPt);
  525.     }
  526.     return false;
  527. }
  528.  
  529.  
  530.  
  531. /*----------------------------------------------------------------------------
  532.     BeginGroupListClick
  533.     
  534.     This function must be called just before calling LClick for a group list
  535.     window.
  536.     
  537.     Exit:    gFirstCall = true, to notify our click loop function that this
  538.                 is the first call after a mouse down.
  539. ----------------------------------------------------------------------------*/
  540.  
  541. void BeginGroupListClick (void)
  542. {
  543.     gFirstCall = true;
  544. }
  545.